/* dxfReader for OpenSceneGraph  Copyright (C) 2005 by GraphArchitecture ( grapharchitecture.com )
 * Programmed by Paul de Repentigny <pdr@grapharchitecture.com>
 *
 * OpenSceneGraph is (C) 2004 Robert Osfield
 *
 * This library is provided as-is, without support of any kind.
 *
 * Read DXF docs or OSG docs for any related questions.
 *
 * You may contact the author if you have suggestions/corrections/enhancements.
 */
#include "dxfReader.h"
#include "dxfDataTypes.h"
#include "codeValue.h"

#include <map>
#include <vector>
#include <iostream>
#include <utility>
#include <sstream>
#include <math.h>
#include <iomanip>

using namespace std;

static std::string
trim(const string &str)
{
    if (!str.size())
        return str;
    string::size_type first = str.find_first_not_of(" \t");
    string::size_type last = str.find_last_not_of("  \t\r\n");
    return str.substr(first, last - first + 1);
}

/*
************** readerBase
*/
bool readerBase::readGroup(std::ifstream &f, codeValue &cv)
{
    cv.reset();
    if (readGroupCode(f, cv._groupCode))
    {
        cv._type = dxfDataType::typeForCode(cv._groupCode);
        switch (cv._type)
        {
        case dxfDataType::BOOL:
            return readValue(f, cv._bool);
            break;
        case dxfDataType::SHORT:
            return readValue(f, cv._short);
            break;
        case dxfDataType::INT:
            return readValue(f, cv._int);
            break;
        case dxfDataType::LONG:
            return readValue(f, cv._long);
            break;
        case dxfDataType::DOUBLE:
            return readValue(f, cv._double);
            break;
        case dxfDataType::UNKNOWN:
        case dxfDataType::STRING:
        case dxfDataType::HEX:
        default: // to do: default case an error
            return readValue(f, cv._string);
            break;
        }
    }
    else
    {
        cv._type = dxfDataType::UNKNOWN;
        cv._groupCode = -1;
    }
    return false;
}

/*
************** readerText
*/

bool readerText::success(bool inSuccess,  string type)
{
    if (!inSuccess)
        cout << "Error converting line " << _lineCount << " to type " << type << endl;
    return inSuccess;
}

bool readerText::getTrimmedLine(std::ifstream &f)
{
    std::string line;
    if (getline(f, line, _delim))
    {
        ++_lineCount;
        _str.clear();
        _str.str(trim(line));
        return true;
    }
    return false;
}

bool readerText::readGroupCode(std::ifstream &f, int &groupcode)
{
    std::string line;
    if (getline(f, line, _delim))
    {
        ++_lineCount;
        line = trim(line);
        if (line.empty())
            return false; // 提前判断空行
        errno = 0;
        char* end;
        long val = std::strtol(line.c_str(), &end, 10);
        if (errno == ERANGE || val > INT_MAX || val < INT_MIN || end == line.c_str()) {
          cout << "Error converting line " << _lineCount << " to type int" << endl;
          return false;
        }
        groupcode = static_cast<int>(val);
        return true;
    }
    return false;
}
bool readerText::readValue(std::ifstream &f, string &s)
{
    std::string line;
    if (getline(f, line, _delim))
    {
        ++_lineCount;
        s = trim(line);
        return true;
    }
    return false;
}
bool readerText::readValue(std::ifstream &f, bool &b)
{
    std::string line;
    if (getline(f, line, _delim))
    {
        ++_lineCount;
        line = trim(line);
        if (line.empty())
        {
            cout << "Error converting line " << _lineCount << " to type bool" << endl;
            return false;
        }
        else
        {
            b = (line == "1") ? true : false;
            return true;
        }
    }
    return false;
}
bool readerText::readValue(std::ifstream &f, short &s)
{
    std::string line;
    if (getline(f, line, _delim))
    {
        ++_lineCount;
        line = trim(line);
        if (line.empty())
            return false; // 提前判断空行
        errno = 0;
        char* end;
        long val = std::strtol(line.c_str(), &end, 10);
        if (errno == ERANGE || val > INT_MAX || val < INT_MIN || end == line.c_str()) {
          cout << "Error converting line " << _lineCount << " to type short" << endl;
          return false;

        }
        s = static_cast<short>(val);
        return true;
    }
    return false;
}
bool readerText::readValue(std::ifstream &f, int &i)
{
    std::string line;
    if (getline(f, line, _delim))
    {
        ++_lineCount;
        line = trim(line);
        if (line.empty())
            return false; // 提前判断空行
        errno = 0;
        char* end;
        long val = std::strtol(line.c_str(), &end, 10);
        if (errno == ERANGE || val > INT_MAX || val < INT_MIN || end == line.c_str()) {
          cout << "Error converting line " << _lineCount << " to type int" << endl;
          return false;

        }
        i = static_cast<int>(val);
        return true;
    }
    return false;
}

bool readerText::readValue(std::ifstream &f, long &l)
{
    std::string line;
    if (getline(f, line, _delim))
    {
        ++_lineCount;
        line = trim(line);
        if (line.empty())
            return false; // 提前判断空行
        errno = 0;
        char* end = nullptr;
        const char* cstr = line.c_str();

        l = std::strtol(cstr, &end, 10);
        if (errno == ERANGE || end == cstr)
        {
          cout << "Error converting line " << _lineCount << " to type long" << endl;
          return false;
        }
        return true;
    }
    return false;
}
bool readerText::readValue(std::ifstream &f, double &d)
{
    std::string line;
    if (getline(f, line, _delim))
    {
        ++_lineCount;
        line = trim(line);
        if (line.empty())
            return false; // 提前判断空行
        char* end;
        errno = 0;  // 清除之前的错误状态
        d = strtod(line.c_str(), &end);
        if (end == line.c_str()|| errno == ERANGE) {
          // 没有任何字符被转换
          cout << "Error converting line " << _lineCount << " to type double" << endl;
          return false;
        }
        return true;
    }
    return false;
}

/*
************** dxfReader
*/

bool dxfReader::openFile(std::string fileName)
{
    if (fileName.size() == 0)
        return false;
    _ifs.open(fileName.c_str(), ios::binary); // found mac autocad with \r delimiters
    if (!_ifs)
    {
        cout << " Can't open " << fileName << endl;
        return false;
    }
    // A binary file starts with "AutoCAD Binary DXF<CR><LF><SUB><NULL>"

    char buf[255];
    _ifs.get(buf, 255);
    string sentinel(buf);
    if (trim(sentinel) == "AutoCAD Binary DXF")
    {
        cout << " Binary DXF not supported. For now. Come back soon." << endl;
        return false;
    }
    // string::size_type lf = sentinel.find('\n');
    // string::size_type cr = sentinel.find('\r');
    //  gossage. un mac peut mettre juste CR. ca fonctionne pas:
    //    if (cr > 0 && (!lf || lf > cr + 1))
    //        _reader = new readerText('\r');
    //    else
    _reader = new readerText;
    _ifs.seekg(0, ios::beg);
    return true;
}

bool dxfReader::nextGroupCode(codeValue &cv)
{
    return (_reader->readGroup(_ifs, cv));
}
